package com.phiture.erp.basic.core.service.impl;

import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.phiture.erp.basic.core.pojo.dto.ErpMaterialCategoryListReqDTO;
import com.phiture.erp.basic.core.pojo.dto.ErpMaterialCategorySaveReqDTO;
import com.phiture.erp.basic.core.service.ErpMaterialCategoryService;
import com.phiture.erp.basic.core.service.ErpMaterialService;
import com.phiture.erp.basic.dal.entity.ErpMaterialCategoryDO;
import com.phiture.erp.basic.dal.mapper.ErpMaterialCategoryMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static com.phiture.erp.common.constants.ErrorCodeConstants.*;

/**
 * @author YH
 * @date 2025/3/31
 */

@Service
@RequiredArgsConstructor
public class ErpMaterialCategoryServiceImpl implements ErpMaterialCategoryService {
    private final ErpMaterialCategoryMapper materialCategoryMapper;
    private final ErpMaterialService materialService;

    @Override
    public Long createMaterialCategory(ErpMaterialCategorySaveReqDTO createReqDTO) {
        // 校验父分类编号的有效性
        validateParentMaterialCategory(null, createReqDTO.getParentId());
        // 校验分类名称的唯一性
        validateMaterialCategoryNameUnique(null, createReqDTO.getParentId(), createReqDTO.getName());
        // 校验分类编码的唯一性
        validateMaterialCategoryCodeUnique(null, createReqDTO.getCode());
        // 插入
        ErpMaterialCategoryDO category = BeanUtils.toBean(createReqDTO, ErpMaterialCategoryDO.class);
        materialCategoryMapper.insert(category);
        // 返回
        return category.getId();
    }

    @Override
    public void updateMaterialCategory(ErpMaterialCategorySaveReqDTO createReqDTO) {
        // 校验存在
        validateMaterialCategoryExists(createReqDTO.getId());
        // 校验父分类编号的有效性
        validateParentMaterialCategory(createReqDTO.getId(), createReqDTO.getParentId());
        // 校验分类名称的唯一性
        validateMaterialCategoryNameUnique(createReqDTO.getId(), createReqDTO.getParentId(), createReqDTO.getName());
        // 校验分类编码的唯一性
        validateMaterialCategoryCodeUnique(createReqDTO.getId(), createReqDTO.getCode());
        // 校验是否有物料
        if (materialService.getMaterialCountByCategoryId(createReqDTO.getId()) > 0) {
            throw exception(MATERIAL_CATEGORY_EXITS_MATERIAL);
        }
        // 更新
        ErpMaterialCategoryDO updateObj = BeanUtils.toBean(createReqDTO, ErpMaterialCategoryDO.class);
        materialCategoryMapper.updateById(updateObj);
    }
    /**
     * 校验物料分类编码唯一性
     *
     * @param id   物料分类编号
     * @param code 物料分类名称
     */
    private void validateMaterialCategoryCodeUnique(Long id, String code) {
        ErpMaterialCategoryDO materialCategory = materialCategoryMapper.selectOne(
                Wrappers.<ErpMaterialCategoryDO>lambdaQuery()
                        .eq(ErpMaterialCategoryDO::getCode, code)
                        .ne(id != null, ErpMaterialCategoryDO::getId, id));
        if (materialCategory != null) {
            throw exception(MATERIAL_CATEGORY_CODE_DUPLICATE);
        }
    }

    @Override
    public void deleteMaterialCategory(Long id) {
        // 1.1 校验存在
        validateMaterialCategoryExists(id);
        // 1.2 校验是否有子物料分类
        if (selectCountByParentId(id) > 0) {
            throw exception(MATERIAL_CATEGORY_EXITS_CHILDREN);
        }
        // 1.3 校验是否有物料
        if (materialService.getMaterialCountByCategoryId(id) > 0) {
            throw exception(MATERIAL_CATEGORY_EXITS_MATERIAL);
        }
        // 2. 删除
        materialCategoryMapper.deleteById(id);
    }

    @Override
    public ErpMaterialCategoryDO getMaterialCategory(Long id) {
        return materialCategoryMapper.selectById(id);
    }

    @Override
    public List<ErpMaterialCategoryDO> getMaterialCategoryList(ErpMaterialCategoryListReqDTO listReqDTO) {
        return materialCategoryMapper.selectList(new LambdaQueryWrapperX<ErpMaterialCategoryDO>()
                .likeIfPresent(ErpMaterialCategoryDO::getName, listReqDTO.getName())
                .eqIfPresent(ErpMaterialCategoryDO::getStatus, listReqDTO.getStatus())
                .orderByDesc(ErpMaterialCategoryDO::getId));
    }

    @Override
    public List<ErpMaterialCategoryDO> getMaterialCategoryList(Collection<Long> ids) {
        return materialCategoryMapper.selectBatchIds(ids);
    }

    @Override
    public Map<Long, ErpMaterialCategoryDO> getMaterialCategoryMap(Collection<Long> ids) {
        return convertMap(getMaterialCategoryList(ids), ErpMaterialCategoryDO::getId);
    }

    @Override
    public Map<Long, String> getMaterialCategoryMap() {
        List<ErpMaterialCategoryDO> list = materialCategoryMapper.selectList(
                Wrappers.lambdaQuery(ErpMaterialCategoryDO.class)
                        .eq(ErpMaterialCategoryDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
                        .eq(ErpMaterialCategoryDO::getDeleted, Boolean.FALSE)
                );
        return convertMap(list, ErpMaterialCategoryDO::getId, ErpMaterialCategoryDO::getName);
    }

    @Override
    public void validateMaterialCategoryExist(Long categoryId) {
        validateMaterialCategoryExists(categoryId);
    }

    private void validateParentMaterialCategory(Long id, Long parentId) {
        if (parentId == null || ErpMaterialCategoryDO.PARENT_ID_ROOT.equals(parentId)) {
            return;
        }
        // 1. 不能设置自己为父物料分类
        if (Objects.equals(id, parentId)) {
            throw exception(MATERIAL_CATEGORY_PARENT_ERROR);
        }
        // 2. 父物料分类不存在
        ErpMaterialCategoryDO parentCategory = materialCategoryMapper.selectById(parentId);
        if (parentCategory == null) {
            throw exception(MATERIAL_CATEGORY_PARENT_NOT_EXITS);
        }
        // 3. 递归校验父物料分类，如果父物料分类是自己的子物料分类，则报错，避免形成环路
        if (id == null) { // id 为空，说明新增，不需要考虑环路
            return;
        }
        for (int i = 0; i < Short.MAX_VALUE; i++) {
            // 3.1 校验环路
            parentId = parentCategory.getParentId();
            if (Objects.equals(id, parentId)) {
                throw exception(MATERIAL_CATEGORY_PARENT_IS_CHILD);
            }
            // 3.2 继续递归下一级父物料分类
            if (parentId == null || ErpMaterialCategoryDO.PARENT_ID_ROOT.equals(parentId)) {
                break;
            }
            parentCategory = materialCategoryMapper.selectById(parentId);
            if (parentCategory == null) {
                break;
            }
        }
    }

    private void validateMaterialCategoryNameUnique(Long id, Long parentId, String name) {
        ErpMaterialCategoryDO materialCategory = selectByParentIdAndName(parentId, name);
        if (materialCategory == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的物料分类
        if (id == null) {
            throw exception(MATERIAL_CATEGORY_NAME_DUPLICATE);
        }
        if (!Objects.equals(materialCategory.getId(), id)) {
            throw exception(MATERIAL_CATEGORY_NAME_DUPLICATE);
        }
    }

    private void validateMaterialCategoryExists(Long id) {
        if (materialCategoryMapper.selectById(id) == null) {
            throw exception(MATERIAL_CATEGORY_NOT_EXISTS);
        }
    }

    private ErpMaterialCategoryDO selectByParentIdAndName(Long parentId, String name){
        return materialCategoryMapper.selectOne(ErpMaterialCategoryDO::getParentId, parentId, ErpMaterialCategoryDO::getName, name);
    }

    private Long selectCountByParentId(Long parentId) {
        return materialCategoryMapper.selectCount(ErpMaterialCategoryDO::getParentId, parentId);
    }
}
